/*
    Contact info:
    bhepple@freeshell.org
    http://bhepple.freeshell.org/scrmgr

    Copyright (C) 1991-2008 Bob Hepple

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; see the file COPYING.  If not, write to
    the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
    Boston, MA 02111-1307, USA.

*/
#include <getopt.h>
#include <setjmp.h>

#include "smlib.h"
#include "smio.h"

CHAR *sm2awk_rcsid(void) {
    return("$Id: sm2awk.c,v 1.8 2008/02/20 13:20:32 bhepple Exp $");
}

extern int	errno;

static CHAR	*progname, pname[9];
static CHAR	rev_string[] = "$Revision: 1.8 $";
static int	print_background = 0;
jmp_buf		NoMoreHeap;

static CHAR *usage_msg[] = {
"",
"Reads SCRMGR files and outputs boiler plate reports for 'awk'",
"-p: include background data in the awk file",
"",
"The 'awk' program produced by this program is designed to recreate the",
"data file. For example, suppose a.dat describes a data file 'a'. Then,",
"running",
"",
"$ sm2awk a.dat >a.awk ",
"",
"produces the 'awk' program 'a.awk'. Running 'awk' ...",
"",
"$ awk -f a.awk a ",
"",
"simply prints 'a'. The idea is to use 'a.awk' as a boilerplate for",
"customising.",
"",
"Note: 'awk' can only cope with fields delimited with the field",
"separator - therefore, SCRMGR 'fixed length' fields do not work in",
"'awk'.",
"",
"Also, SCRMGR can accept nearly any character in a field name while",
"'awk' cannot (e.g. the minus sign '-'). This program converts names",
"appropriately.",
"",NULL
};

static void usage()
{
    CHAR **c;
    
    fprintf(stderr, "%s of %s.\n", rev_string, pname);
    fprintf(stderr, "Usage: %s [-p] file\n", pname);
    
    c = usage_msg;
    while (*c) {
        fputs(*c, stderr);
        fputc('\n', stderr);
        c++;
    }
    exit(2);
}

/* stub */
void sm_error_exit ARG1(CHAR *, msg)
{
}

/* Stubs to be overidden if needed (at the moment, only if your application
   uses field triggers) */

f_ptr sm_get_field_trigger ARG1(SM_FIELD, field)
{
    return(NULL);
}

static void print_bgrd ARG1(SM_SCREEN, screen)
{
    /* This assumes that the screen fields and background records are sorted
       This routine is more gruesome - it looks at the internals of the 
       field records. */

    SM_FIELD		*fieldp;
    SM_BACKGROUND	*bgp;
    int			row;
    CHAR		*bckgrd, *arglist, temp[20], *p, *d, *e;
    int			chars_deleted = 0; /* Could be < 0 */

    bckgrd  = alloca(SM_MAXREC);
    arglist = alloca(SM_MAXREC);

    fieldp = sm_first_fld(screen);
    bgp = sm_first_bg(screen);
    row =0;

    while ((fieldp && *fieldp) || (bgp && *bgp)) {
	memset(bckgrd, ' ', SM_MAXREC);
	/* Bit of a kludge for the worst case - leave a bit of room for 
	   inserting lots of %-1s strings for fields of length 1 */
	bckgrd[SM_MAXREC - 100] = 0; 
	*arglist = 0;

	/* Put the background in ... */
	while (bgp && *bgp && ((*bgp)->row == row)) {
	    /* Copy excluding the null ... */
	    p = StringToStr((*bgp)->text);
	    d = bckgrd;
	    while (*p)
		*d++ = *p++;
	    bgp = sm_next_bg(bgp);
	}

	chars_deleted = 0;

	/* Now put in the fields */
	while (fieldp && *fieldp && ((*fieldp)->row == row)) {
	    sprintf(temp, "%%-%d.%ds", abs((*fieldp)->len), abs((*fieldp)->len));
	    /* Now remove 'len' characters from the background */
	    d = bckgrd + abs((*fieldp)->col) - chars_deleted;
	    p = d + abs((*fieldp)->len);
	    while (*p)
		*d++ = *p++;
	    *d = 0;

	    /* Now insert the format string */
	    /* First make a hole */
	    p = bckgrd + strlen(bckgrd);
	    d = p + strlen(temp);
	    e = bckgrd + abs((*fieldp)->col) - chars_deleted;
	    while (p >= e)
		*d-- = *p--;
	    
	    /* Now put the format string in */
	    p = temp;
	    d = e;
	    while (*p)
		*d++ = *p++;

	    /* Now add the field name to the argument list */
	    strcat(arglist, ", ");
	    strcat(arglist, (*fieldp)->field_name);

	    chars_deleted += (abs((*fieldp)->len) - strlen(temp));

	    fieldp = sm_next_fld(fieldp);
	}

	/* Right trim the background ... */
	p = bckgrd + strlen(bckgrd) - 1;
	while ((p > bckgrd) && isspace(*p))
	    *p-- = 0;

	/* Now write the awk command */
	printf("printf(\"");
	printf("%s", bckgrd);
	printf("\\n\"%s", arglist);
	printf(")\n");

	row++;
    }
    /* Final touch - have the awk script output a form feed */
    printf("printf(\"%c\")\n", 014);
}

static void print_field ARG2(SM_FIELD, field, int, fieldno)
{
    CHAR	*old_fn;

    old_fn = alloca(100);

    if (field->len < 0)
	fprintf(stderr,
		"Warning: field \"%s\"\n in screen \"%s\" is fixed length.\n",
		field->field_name, field->parent->screen_name);
    strcpy(old_fn, field->field_name);
    if (sm_fix_name(field->field_name))
	fprintf(stderr, "Warning: field \"%s\" changed to \"%s\".\n",
		old_fn, field->field_name);
    printf("%s=$%d\n", field->field_name, fieldno++);
}

static int process()
{
    SM_SCREEN	screen, *screenp;
    SM_FIELD	*fieldp, field;
    int		errors = 0;
    int		fieldno;
    int		tag_number = -1;

    screenp = sm_first_scr(sm_user_screens());
    if (!screenp || !*screenp)
        return(errors);

    if (sm_user_screens()->separator == ' ')
        printf("BEGIN\n");
    else if (sm_user_screens()->separator == 0) {
        printf("BEGIN {FS=\"\\n\"}\n");
        sm_user_screens()->separator = '\n';
    } else if (sm_user_screens()->separator == '\t')
        printf("BEGIN {FS=\"\\t\"}\n");
    else
        printf("BEGIN {FS=\"%c\"}\n", sm_user_screens()->separator);

    /* Find out tag number ... */
    if (*screenp && sm_user_screens()->tagname) {
        fieldno = 1;
        screen = *screenp;
        tag_number = -1;
        if (screen->data_field) {
            CHAR	**name;

            for (name = (CHAR **) firstArrayItemp(screen->data_field);
                 name && *name;
                 name = (CHAR **) nextArrayItemp(screen->data_field, (void **)name)) {
                if (strcmp(sm_user_screens()->tagname, *name) == 0) {
                    tag_number = fieldno;
                    break;
                }
                fieldno++;
            }
        } else { /* Use ordering from the screen */
            for (fieldp = sm_first_fld(screen); 
                 fieldp && *fieldp; 
                 fieldp = sm_next_fld(fieldp)) {
                if (strcmp(sm_user_screens()->tagname, 
                        (*fieldp)->field_name) == 0) {
                    tag_number = fieldno;
                    break;
                }
                fieldno++;
            }
        }
        if (tag_number < 0) {
            fprintf(stderr, "Can't find a field for tag: %s\n",
                sm_user_screens()->tagname);
            errors++;
        }
    }

    while (*screenp) {
        screen = *screenp;
    	printf("#Screen: %s\n", screen->screen_name);
        if (sm_user_screens()->tagname)
            printf("$%d == \"%s\" ", 
                tag_number, screen->screen_name);
        printf("{\n");
        fieldno = 1;
        if (screen->data_field) {
            CHAR	**name;

            for (name = (CHAR **) firstArrayItemp(screen->data_field);
                 name && *name;
                 name = (CHAR **) nextArrayItemp(screen->data_field, (void **)name)) {
                if ((field = sm_fld_open(screen, *name)) == NULL)
                    fprintf(stderr, "Warning: can't open field \"%s\".\n", *name);
                else
                    print_field(field, fieldno++);
            }
        } else {
            for (fieldp = sm_first_fld(screen); 
                 fieldp && *fieldp; 
                 fieldp = sm_next_fld(fieldp))
                print_field(*fieldp, fieldno++);
        }

        if (print_background)
            print_bgrd(screen);
        else {
            for (fieldp = sm_first_fld(screen); 
                 fieldp && *fieldp; 
                 fieldp = sm_next_fld(fieldp)) {
                field = *fieldp;
                if (sm_user_screens()->separator == '\t')
                    printf("printf(\"%%s\\t\", %s)\n",
                        field->field_name);
                else if (sm_user_screens()->separator == '\n')
                    printf("printf(\"%%s\\n\", %s)\n",
                        field->field_name);
                else
                    printf("printf(\"%%s%c\", %s)\n",
                        sm_user_screens()->separator,
                        field->field_name);
            }
            printf("printf(\"\\n\")\n");
        }
        printf("}\n");
        screenp = sm_next_scr(screenp);
    }
    return(errors);
}

int main(argc, argv)
    int		argc;
    CHAR	**argv;
{
    int		c,
    		errors = 0;
    
    progname = argv[0];
    fname(pname, progname);
    
    while ((c = getopt(argc, argv, "hp?")) != EOF)
        switch (c) {
	case 'p':
	    print_background = 1;
	    break;
        case '?':
        default:
            usage();
            break;
        }

    if (setjmp(NoMoreHeap)) {
	fprintf(stderr, "Ran out of heap!\n");
	exit(1);
    }

    sm_init(0, argc, argv);
    smio_term();

    if (optind >= argc)
	usage();
    else
        for (; optind < argc; optind++) {
	    sm_clear_out(0);
	    if (sm_fopen(sm_user_screens(), argv[optind]) == SM_ERROR)
		usage();
	    else
		errors += process();
	}

    exit(errors);
}


/* For emacs:
 * Local Variables:
 * tab-width: 8
 * End:
 */
